package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"log"
	"math"
	"net"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

//文件路径
var filePath = "out.txt"

// 文件资源
var fileResource *os.File

//缓冲区
var fileBuf *bufio.Reader

// 协程等待
var wg sync.WaitGroup

func main() {

	var startTime = time.Now()

	var err error

	fileResource, err = os.Open(filePath)

	if err != nil {
		log.Fatalln(err)
	}
	//建立缓冲区，把文件内容放到缓冲区中
	fileBuf = bufio.NewReader(fileResource)

	cpus := runtime.NumCPU() * 2
	for i := 0; i < cpus; i++ {
		wg.Add(1)
		go handle(i)
	}

	wg.Wait()
	_ = fileResource.Close()

	fmt.Println("数据处理结束，准备导出到文件")
	fmt.Println("开始输出新规则")
	f, err := os.Create(filePath + ".new.txt") //创建文件
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	w := bufio.NewWriter(f)
	for _, v := range ips {
		_, err = w.WriteString(v.Line)
	}
	_ = w.Flush()

	fmt.Println("开始输出废弃规则")
	f2, err := os.Create(filePath + ".del.txt") //创建文件
	if err != nil {
		log.Fatal(err)
	}
	defer f2.Close()
	w2 := bufio.NewWriter(f)
	f2buf, err := json.Marshal(ipds)
	if err != nil {
		log.Fatal(err)
	}

	_, err = w2.Write(f2buf)
	if err != nil {
		log.Fatal(err)
	}
	_ = w2.Flush()
	endTime := time.Now()
	fmt.Println("程序开始时间", startTime)
	fmt.Println("程序结束时间", endTime)
	fmt.Println("程序耗时", endTime.Sub(startTime))

}

func handle(i int) {
	defer wg.Done()
	for {
		fmt.Printf("协程 %d 准备读取新数据\n", i)
		line, lineNumber, err := readLine(i)
		if err != nil {
			log.Printf("协程 %d 退出", i)
			break
		}
		mIpS(i, line, lineNumber)
	}

}

type ipInfo struct {
	Ip    int64
	Mask  int
	Line  string
	Index uint32
}
type ipInfoDel struct {
	Index1 uint32
	Line1  string
	Index2 uint32
	Line2  string
}

//保留的数据
var ips []ipInfo

// 被删除的数据
var ipds []ipInfoDel
var ipsLock sync.Mutex

func mIpS(i int, line string, lineNumber uint32) {
	_ipInfo := strings.Split(line, "|")
	ipaddress := ip2Int(_ipInfo[0])
	mask, err := strconv.Atoi(_ipInfo[3])
	if err != nil {
		mask = 0
	}

	ipsLock.Lock()
	defer ipsLock.Unlock()
	fmt.Printf("协程 %d 开始匹配 第 %d 行\n", i, lineNumber)

	for index, _tmp := range ips {
		_mask := 32 - math.Min(float64(mask), float64(_tmp.Mask))
		ip1 := ipaddress >> int(_mask)
		ip2 := _tmp.Ip >> int(_mask)

		if ip1 == ip2 {
			if mask < _tmp.Mask {
				ipds = append(ipds, ipInfoDel{Index1: _tmp.Index, Line1: _tmp.Line, Index2: lineNumber, Line2: line})
				log.Printf("协程 %d  第 %d 行 与 第 %d 行匹配到冲突,进行替换\n", i, lineNumber, _tmp.Index)
				ips[index] = ipInfo{Ip: ipaddress, Mask: mask, Line: line, Index: lineNumber}
			} else {
				log.Printf("协程 %d  第 %d 行 与 第 %d 行匹配到冲突,保持不变 \n", i, lineNumber, _tmp.Index)
			}
			return

		}

	}
	fmt.Printf("协程 %d 第 %d 行，已加入列表\n", i, lineNumber)
	ips = append(ips, ipInfo{Ip: ipaddress, Mask: mask, Line: line, Index: lineNumber})
}

var fileBufReadLock sync.Mutex
var readLineCount uint32 = 0

func readLine(i int) (string, uint32, error) {
	// 开启读锁
	fileBufReadLock.Lock()
	// 解锁
	defer fileBufReadLock.Unlock()
	readLineCount++
	fmt.Printf("协程 %d 读取第 %d 行\n", i, readLineCount)
	//遇到\n结束读取
	b, errR := fileBuf.ReadBytes('\n')
	if errR != nil {
		log.Printf("协程 %d 遇到错误或读取完成，退出\n", i)
		return string(b), readLineCount, errR
	}

	return string(b), readLineCount, errR
}

// Convert uint to net.IP http://www.sharejs.com
func int2Ip(ipnr int64) net.IP {
	var bytes [4]byte
	bytes[0] = byte(ipnr & 0xFF)
	bytes[1] = byte((ipnr >> 8) & 0xFF)
	bytes[2] = byte((ipnr >> 16) & 0xFF)
	bytes[3] = byte((ipnr >> 24) & 0xFF)

	return net.IPv4(bytes[3], bytes[2], bytes[1], bytes[0])
}

// Convert net.IP to int64 ,  http://www.sharejs.com
func ip2Int(ipnr string) int64 {
	bits := strings.Split(ipnr, ".")

	b0, _ := strconv.Atoi(bits[0])
	b1, _ := strconv.Atoi(bits[1])
	b2, _ := strconv.Atoi(bits[2])
	b3, _ := strconv.Atoi(bits[3])

	var sum int64

	sum += int64(b0) << 24
	sum += int64(b1) << 16
	sum += int64(b2) << 8
	sum += int64(b3)

	return sum
}
